Все мы знаем хорошо известную функцию VLOOKUP(), которая помогает нам совмещать данные из разных таблиц. Однако данная функция имеет один существенный недостаток – она не может совмещать подобные значения, то есть если в слове допущена ошибка – то совпадения уже не будет.
Чтобы иметь возможность совмещать приблизительные значения, мы можем создать собственную функцию. Давайте назовем ее FuzzyLookup().
Представим, что у нас есть два списка. И в том, и в другом примерно одни и те же элементы, но записаны они могут быть немного по-разному. Задача - подобрать каждому элементу в первом списке максимально схожий элемент из второго списка, т.е. реализовать поиск ближайшего максимально схожего текста.
Большой вопрос, в данном случае, что считать критерием "схожести". Просто количество совпадающих символов? Или количество совпадений, идущих подряд? Учитывать ли регистр символов или пробелы? Что делать с разным расположением слов во фразе? Вариантов много и однозначного решения нет – для каждой ситуации тот или иной будет лучше других.
В нашем случае мы реализуем самый простой вариант – поиск по максимальному количеству совпадений символов. Он не идеален, но для большинства ситуаций работает вполне надежно.
Чтобы добавить функцию FuzzyLookup , откройте меню Tools - Macros - Edit Macros... , выберите Module1 и скопируйте следующий текст в модуль:
Function FuzzyLOOKUP(LookupValue As String, SrcTable As Variant, Optional SimThreshold As Single) As String
' moonexcel.com.ua
Dim Str As String
Dim CellArray As Variant
Dim StrArray As Variant
If IsMissing(SimThreshold) Then SimThreshold = 0
Str = LCase(LookupValue)
StrArray = Split(Str)
StrExt = UBound(StrArray)
For Each Cell In SrcTable
CellArray = Split(LCase(Cell))
CellExt = UBound(CellArray)
CellRate = 0
' Проверяем каждое слово в поисковой фразе
For x = 0 To StrExt
StrWord = StrArray(x)
If Len(StrWord) = 0 Then GoTo continue_x
MaxStrWordRate = 0
' Проверяем каждое слово в очередной ячейке из исходной таблицы значений
For i = 0 To CellExt
CellWord = CellArray(i)
If Len(CellWord) = 0 Then GoTo continue_i
FindCharNum = OccurrenceNum(StrWord, CellWord)
StrWordRate = FindCharNum / Max(Len(StrWord),Len(CellWord))
If StrWordRate > MaxStrWordRate Then MaxStrWordRate = StrWordRate
continue_i:
Next i
CellRate = CellRate + MaxStrWordRate
continue_x:
Next x
' Сохраняем лучшее совпадение
If CellRate > MaxCellRate Then
MaxCellRate = CellRate
BestCell = Cell
FindCharNum = OccurrenceNum(Str, Cell)
SimRate = FindCharNum / Max(Len(Str),Len(Cell))
End If
Next Cell
IF SimRate >= SimThreshold Then
IF SimThreshold = -1 Then
ReturnValue = BestCell + " (" + Format(SimRate, "0.00") + ")"
ElseIf SimThreshold = -2 Then
ReturnValue = Format(SimRate, "0.00")
Else
ReturnValue = BestCell
End If
Else
ReturnValue = ""
End If
FuzzyLOOKUP = ReturnValue
End Function
Function OccurrenceNum(ByVal SourceString As String, ByVal TargetString As String)
For i = 1 To Len(SourceString)
' Ищем вхождение каждого символа
Position = InStr(1, TargetString, Mid(SourceString, i, 1), 1)
' Увеличиваем счетчик совпадений
If Position > 0 Then
Count = Count + 1
' Удаляем найденный символ
TargetString = Left(TargetString, Position - 1) + Right(TargetString, Len(TargetString) - Position)
End If
Next i
OccurrenceNum = Count
End Function
Function Max(ByVal value1 As Variant, ByVal value2 As Variant)
If value1 > value2 Then
Result = value1
Else
Result = value2
End If
Max = Result
End Function
Далее, закройте Macro Editor и вернитесь на рабочий лист LibreOffice Calc - теперь вы можете воспользоваться нашей новой функцией FuzzyLookup() .
Вы также можете воспользоваться функцией FUZZYLOOKUP() установив бесплатное расширение YouLibreCalc.oxt или его полнофункциональную версию YLC_Utilities.oxt .
После этого данная функция будет доступна во всех файлах, которые будут открыты в LibreOffice Calc.